﻿using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ArtMath.PointCollection
{
    [Serializable]
    public abstract class PointCollectionBase
    {
        public HashSet<PointCollectionBase> Exceptions;
        protected abstract PointCollectionBase Intersect(PointCollectionBase collection);//不能公开，因为是单向交集
        public abstract PointD? GetNearestPoint(PointD pt);
        public abstract bool Contains(PointD pt);
        public virtual PointD? HeadPoint
        {
            get { return null; }
        }
        public virtual PointD? TailPoint
        {
            get { return null; }
        }
        public virtual PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        {
            reverse = false;
            return currentLocation;
        }
        public virtual double GetSlideExcess(PointD currentLocation, double length)
        {
            return double.NegativeInfinity;
        }
        internal PointCollectionBase AfterIntersect(PointCollectionBase result)
        {
            if (Exceptions != null)
            {
                if (result.Exceptions == null)
                {
                    result.Exceptions = new HashSet<PointCollectionBase>(Exceptions);
                }
                else
                {
                    result.Exceptions.AddRange(Exceptions);
                }
            }
            return result;
        }
        protected bool AfterContains(PointD pt)
        {
            if (Exceptions != null)
            {
                foreach (PointCollectionBase item in Exceptions)
                {
                    if (item.Contains(pt))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        public static PointCollectionBase Intersect(PointCollectionBase pc1, PointCollectionBase pc2)
        {
            PointCollectionBase tmp = pc1.Intersect(pc2);
            if (tmp == Empty.Instance)
            {
                tmp = pc2.Intersect(pc1);
            }
            return tmp;
        }
        public static PointCollectionBase[] Intersect(IEnumerable<PointCollectionBase> pcs1, IEnumerable<PointCollectionBase> pcs2)
        {
            if (pcs1 != null && pcs2 != null)
            {
                List<PointCollectionBase> res = new List<PointCollectionBase>();
                foreach (PointCollectionBase item1 in pcs1)
                {
                    foreach (PointCollectionBase item2 in pcs2)
                    {
                        PointCollectionBase tmp = Intersect(item1, item2);
                        if (tmp != Empty.Instance)
                        {
                            res.Add(tmp);
                        }
                    }
                }
                if (res.Count > 0)
                {
                    return res.ToArray();
                }
            }
            return null;
        }
        public CustomCollection CustomIntersect(PointCollectionBase collection)
        {
            return new CustomCollection((PointD pt) =>
            {
                PointD? pt1 = GetNearestPoint(pt), pt2 = collection.GetNearestPoint(pt);
                double d1 = pt1.HasValue && collection.Contains(pt1.Value) ? pt1.Value.DistanceTo(pt) : double.PositiveInfinity,
                d2 = pt2.HasValue && Contains(pt2.Value) ? pt2.Value.DistanceTo(pt) : double.PositiveInfinity;
                if (double.IsPositiveInfinity(d1) && double.IsPositiveInfinity(d2)) return null;
                return d1 < d2 ? pt1 : pt2;
            },
            (PointD pt) =>
            {
                return Contains(pt) && collection.Contains(pt);//此句决定结果不需要afterintersect
            });
        }
        //public override bool Equals(object obj)
        //{
        //    PointCollectionBase pc = obj as PointCollectionBase;
        //    if (pc != null)
        //    {
        //        return Exceptions.Count == pc.Exceptions.Count && Exceptions.All(pc.Exceptions.Contains);
        //    }
        //    return base.Equals(obj);
        //}
        //public override int GetHashCode()
        //{
        //    return base.GetHashCode();
        //}
    }
}
